#include "compi/context.h"
#include "compi/tag.h"

#include <gtest/gtest.h>

#include <mpi.h>

using namespace compi;

class TagLeaseTest : public ::testing::Test
{
protected:
    void SetUp() override
    {
        int initialized = 0;
        MPI_Initialized(&initialized);
        if (!initialized)
        {
            int provided;
            MPI_Init_thread(nullptr, nullptr, MPI_THREAD_MULTIPLE, &provided);
        }
    }

    void TearDown() override { Context::destroy_for_comm(MPI_COMM_WORLD); }
};

TEST_F(TagLeaseTest, BasicLeaseRetire)
{
    auto & allocator = Context::for_comm(MPI_COMM_WORLD).tag_allocator();
    auto ns = allocator.create_namespace("lease_basic");

    auto lease = ns.lease();
    ASSERT_TRUE(lease.active());
    int tag_val = lease.value();
    ASSERT_TRUE(ns.owns(tag_val));

    lease.retire();
    ASSERT_FALSE(lease.active());

    // 释放后应可重用同一标签（LIFO 回收）
    auto tag = ns.request();
    EXPECT_EQ(tag.value, tag_val);
}

TEST_F(TagLeaseTest, RenewKeepsActive)
{
    auto & allocator = Context::for_comm(MPI_COMM_WORLD).tag_allocator();
    auto ns = allocator.create_namespace("lease_renew");

    auto lease = ns.lease();
    ASSERT_TRUE(lease.active());
    int v1 = lease.value();

    bool ok = lease.renew();
    ASSERT_TRUE(ok);
    ASSERT_TRUE(lease.active());
    int v2 = lease.value();
    ASSERT_TRUE(ns.owns(v2));

    // 两次值可以相同或不同，取决于分配策略；只验证均有效
    (void)v1;
}

TEST_F(TagLeaseTest, MoveSemanticsTransferOwnership)
{
    auto & allocator = Context::for_comm(MPI_COMM_WORLD).tag_allocator();
    auto ns = allocator.create_namespace("lease_move");

    TagLease lease1 = ns.lease();
    int v1 = lease1.value();

    TagLease lease2 = std::move(lease1);
    EXPECT_FALSE(lease1.active());
    EXPECT_TRUE(lease2.active());
    EXPECT_EQ(lease2.value(), v1);

    lease2.retire();
    // 重用验证
    auto tag = ns.request();
    EXPECT_EQ(tag.value, v1);
}

TEST_F(TagLeaseTest, DestructorReleasesTag)
{
    auto & allocator = Context::for_comm(MPI_COMM_WORLD).tag_allocator();
    auto ns = allocator.create_namespace("lease_dtor");
    int captured = -1;
    {
        auto lease = ns.lease();
        captured = lease.value();
        ASSERT_TRUE(ns.owns(captured));
        // 作用域结束时自动释放
    }
    auto tag = ns.request();
    EXPECT_EQ(tag.value, captured);
}